package com.samp.lang.classloader;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;

public class EncryptClass {
    private static String algorithm = "DES";
    private static String class_name = "com.samp.lang.classloader.EncryptServiceImpl";
    private static String des_key = "F85875C89D627354";

    public static void encrypt(String classPath, String key)throws Exception{
        File classFile = new File(classPath);
        if (!classFile.exists()) {
            System.out.println("类文件不存在，path="+classPath);
            return;
        }
        String encryptClass = classFile.getParent() + File.separator + "encrypted"  + File.separator + classFile.getName();
        try{
            byte[] classBytes = transferFileToByte(classFile);
            Cipher cipher = Cipher.getInstance(algorithm);
            SecretKey secretKey = new SecretKeySpec(parseHexStr2Byte(key), algorithm);
            cipher.init(Cipher.ENCRYPT_MODE,secretKey );
            byte[] result = cipher.doFinal(classBytes);
            //加密后的bytes保存至文件。
            InputStream in = new ByteArrayInputStream(result);
            File encryptFile=new File(encryptClass);
            FileOutputStream fos=new FileOutputStream(encryptFile);
            byte[] b = new byte[2048];
            int nRead = 0;
            while ((nRead = in.read(b)) != -1) {
                fos.write(b, 0, nRead);
            }
            fos.flush();
            fos.close();
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static byte[] decrypt(String classPath, String key)throws Exception{
        File classFile = new File(classPath);
        if (!classFile.exists()) {
            System.out.println("加密的类文件不存在，path="+classPath);
            return null;
        }
        try{
            byte[] classBytes = transferFileToByte(classFile);
            Cipher cipher = Cipher.getInstance(algorithm);
            SecretKey secretKey = new SecretKeySpec(parseHexStr2Byte(key), algorithm);
            cipher.init(Cipher.DECRYPT_MODE,secretKey );
            return cipher.doFinal(classBytes);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] transferFileToByte(File file) {
        byte[] by = new byte[(int) file.length()];
        try {
            InputStream is = new FileInputStream(file);
            ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
            byte[] bb = new byte[2048];
            int ch;
            ch = is.read(bb);
            while (ch != -1) {
                bytestream.write(bb, 0, ch);
                ch = is.read(bb);
            }
            by = bytestream.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return by;
    }

    public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length()/2];
        for (int i = 0;i< hexStr.length()/2; i++) {
            int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
            int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    private static String getClassRealPath(){
        String[] prefix_path = {System.getProperty("user.dir"), "target","classes"};
        String[] paths = class_name.split("\\.");
        String[] real_path = new String[prefix_path.length+paths.length];
        System.arraycopy(prefix_path,0,real_path,0,prefix_path.length);
        System.arraycopy(paths,0,real_path,prefix_path.length,paths.length);
        return String.join(File.separator,real_path)+".class";
    }

    public static void setAlgorithm(String algorithm) {
        EncryptClass.algorithm = algorithm;
    }

    public static String getAlgorithm() {
        return algorithm;
    }

    public static void main(String[] args) {
        try {
            EncryptClass.encrypt(getClassRealPath(),EncryptClass.des_key);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
